package com.cdxy.util;

import org.apache.tomcat.util.codec.binary.Base64;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * @Author: tangy
 * @Date: 2020/3/7 17:51
 */

public class AlgorithmUtil {
    public AlgorithmUtil() {
    }

    public static class Unicode {
        public Unicode() {
        }

        public static String toUtf8(String encodeString) {
            int len = encodeString.length();
            StringBuilder outBuffer = new StringBuilder(len);
            int x = 0;

            while (true) {
                while (true) {
                    while (x < len) {
                        char aChar = encodeString.charAt(x++);
                        if (aChar == '\\') {
                            aChar = encodeString.charAt(x++);
                            if (aChar == 'u') {
                                int value = 0;

                                for (int i = 0; i < 4; ++i) {
                                    aChar = encodeString.charAt(x++);
                                    switch (aChar) {
                                        case '0':
                                        case '1':
                                        case '2':
                                        case '3':
                                        case '4':
                                        case '5':
                                        case '6':
                                        case '7':
                                        case '8':
                                        case '9':
                                            value = (value << 4) + aChar - 48;
                                            break;
                                        case ':':
                                        case ';':
                                        case '<':
                                        case '=':
                                        case '>':
                                        case '?':
                                        case '@':
                                        case 'G':
                                        case 'H':
                                        case 'I':
                                        case 'J':
                                        case 'K':
                                        case 'L':
                                        case 'M':
                                        case 'N':
                                        case 'O':
                                        case 'P':
                                        case 'Q':
                                        case 'R':
                                        case 'S':
                                        case 'T':
                                        case 'U':
                                        case 'V':
                                        case 'W':
                                        case 'X':
                                        case 'Y':
                                        case 'Z':
                                        case '[':
                                        case '\\':
                                        case ']':
                                        case '^':
                                        case '_':
                                        case '`':
                                        default:
                                            throw new IllegalArgumentException("Malformed   \\uxxxx   encoding.");
                                        case 'A':
                                        case 'B':
                                        case 'C':
                                        case 'D':
                                        case 'E':
                                        case 'F':
                                            value = (value << 4) + 10 + aChar - 65;
                                            break;
                                        case 'a':
                                        case 'b':
                                        case 'c':
                                        case 'd':
                                        case 'e':
                                        case 'f':
                                            value = (value << 4) + 10 + aChar - 97;
                                    }
                                }

                                outBuffer.append((char) value);
                            } else {
                                if (aChar == 't') {
                                    aChar = '\t';
                                } else if (aChar == 'r') {
                                    aChar = '\r';
                                } else if (aChar == 'n') {
                                    aChar = '\n';
                                } else if (aChar == 'f') {
                                    aChar = '\f';
                                }

                                outBuffer.append(aChar);
                            }
                        } else {
                            outBuffer.append(aChar);
                        }
                    }

                    return outBuffer.toString();
                }
            }
        }
    }

    public static class SHA1 {
        private static final char[] HEX_DIGITS = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

        public SHA1() {
        }

        private static String getFormattedText(byte[] bytes) {
            int len = bytes.length;
            StringBuilder buf = new StringBuilder(len * 2);
            byte[] var3 = bytes;
            int var4 = bytes.length;

            for (int var5 = 0; var5 < var4; ++var5) {
                byte aByte = var3[var5];
                buf.append(HEX_DIGITS[aByte >> 4 & 15]);
                buf.append(HEX_DIGITS[aByte & 15]);
            }

            return buf.toString();
        }

        public static String encode(String data) {
            if (data == null) {
                return null;
            } else {
                try {
                    MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
                    messageDigest.update(data.getBytes());
                    return getFormattedText(messageDigest.digest());
                } catch (Exception var2) {
                    throw new RuntimeException(var2);
                }
            }
        }
    }

    public static class Rsa {
        public Rsa() {
        }

        public static PublicKey getPublicKey(String pubKey) {
            PublicKey publicKey = null;

            try {
                X509EncodedKeySpec bobPubKeySpec = new X509EncodedKeySpec((new BASE64Decoder()).decodeBuffer(pubKey));
                KeyFactory keyFactory = KeyFactory.getInstance("RSA");
                publicKey = keyFactory.generatePublic(bobPubKeySpec);
            } catch (NoSuchAlgorithmException | InvalidKeySpecException | IOException var4) {
                var4.printStackTrace();
            }

            return publicKey;
        }

        public static PrivateKey getPrivateKey(String priKey) {
            PrivateKey privateKey = null;

            try {
                PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec((new BASE64Decoder()).decodeBuffer(priKey));
                KeyFactory keyFactory = KeyFactory.getInstance("RSA");
                privateKey = keyFactory.generatePrivate(priPKCS8);
            } catch (NoSuchAlgorithmException | InvalidKeySpecException | IOException var4) {
                var4.printStackTrace();
            }

            return privateKey;
        }

        public static String publicEncrypt(String data, RSAPublicKey publicKey) {
            try {
                Cipher cipher = Cipher.getInstance("RSA");
                cipher.init(1, publicKey);
                return Base64.encodeBase64URLSafeString(rsaSplitCodec(cipher, 1, data.getBytes(StandardCharsets.UTF_8), publicKey.getModulus().bitLength()));
            } catch (Exception var3) {
                throw new RuntimeException("加密字符串[" + data + "]时遇到异常", var3);
            }
        }

        public static String privateDecrypt(String data, RSAPrivateKey privateKey) {
            try {
                Cipher cipher = Cipher.getInstance("RSA");
                cipher.init(2, privateKey);
                return new String(rsaSplitCodec(cipher, 2, Base64.decodeBase64(data), privateKey.getModulus().bitLength()), "utf-8");
            } catch (Exception var3) {
                throw new RuntimeException("私钥解密字符串[" + data + "]时遇到异常", var3);
            }
        }

        public static String privateEncrypt(String data, RSAPrivateKey privateKey) {
            try {
                Cipher cipher = Cipher.getInstance("RSA");
                cipher.init(1, privateKey);
                return Base64.encodeBase64URLSafeString(rsaSplitCodec(cipher, 1, data.getBytes("utf-8"), privateKey.getModulus().bitLength()));
            } catch (Exception var3) {
                throw new RuntimeException("加密字符串[" + data + "]时遇到异常", var3);
            }
        }

        public static String publicDecrypt(String data, RSAPublicKey publicKey) {
            try {
                Cipher cipher = Cipher.getInstance("RSA");
                cipher.init(2, publicKey);
                return new String(rsaSplitCodec(cipher, 2, Base64.decodeBase64(data), publicKey.getModulus().bitLength()), "utf-8");
            } catch (Exception var3) {
                throw new RuntimeException("公钥解密字符串[" + data + "]时遇到异常", var3);
            }
        }

        private static byte[] rsaSplitCodec(Cipher cipher, int opmode, byte[] data, int keySize) {
            int maxBlock;
            if (opmode == 2) {
                maxBlock = keySize / 8;
            } else {
                maxBlock = keySize / 8 - 11;
            }

            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int offSet = 0;
            int i = 0;

            try {
                while (data.length > offSet) {
                    byte[] buff;
                    if (data.length - offSet > maxBlock) {
                        buff = cipher.doFinal(data, offSet, maxBlock);
                    } else {
                        buff = cipher.doFinal(data, offSet, data.length - offSet);
                    }

                    out.write(buff, 0, buff.length);
                    ++i;
                    offSet = i * maxBlock;
                }
            } catch (Exception var10) {
                throw new RuntimeException("加解密阀值为[" + maxBlock + "]的数据时发生异常", var10);
            }

            byte[] resultData = out.toByteArray();
            IOUtils.closeQuietly(out);
            return resultData;
        }
    }

    public static class Des {
        public Des() {
        }

        public static String encrypt(String data, String key) throws Exception {
            byte[] bt = encrypt(data.getBytes(), key.getBytes());
            return (new BASE64Encoder()).encode(bt);
        }

        public static String decrypt(String data, String key) throws IOException, Exception {
            if (data == null) {
                return null;
            } else {
                BASE64Decoder decoder = new BASE64Decoder();
                byte[] buf = decoder.decodeBuffer(data);
                byte[] bt = decrypt(buf, key.getBytes());
                return new String(bt);
            }
        }

        private static byte[] encrypt(byte[] data, byte[] key) throws Exception {
            SecureRandom sr = new SecureRandom();
            DESKeySpec dks = new DESKeySpec(key);
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            SecretKey secureKey = keyFactory.generateSecret(dks);
            Cipher cipher = Cipher.getInstance("DES");
            cipher.init(1, secureKey, sr);
            return cipher.doFinal(data);
        }

        private static byte[] decrypt(byte[] data, byte[] key) throws Exception {
            SecureRandom sr = new SecureRandom();
            DESKeySpec dks = new DESKeySpec(key);
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            SecretKey securekey = keyFactory.generateSecret(dks);
            Cipher cipher = Cipher.getInstance("DES");
            cipher.init(2, securekey, sr);
            return cipher.doFinal(data);
        }
    }

    public static class Md5 {
        public Md5() {
        }

        public static String encode(String data) {
            MessageDigest digest;
            try {
                digest = MessageDigest.getInstance("MD5");
            } catch (NoSuchAlgorithmException var3) {
                throw new IllegalStateException("MD5 algorithm not available.  Fatal (should be in the JDK).");
            }

            byte[] bytes = digest.digest(data.getBytes(StandardCharsets.UTF_8));
            return String.format("%032x", new BigInteger(1, bytes));
        }
    }
}
